3D Graphics Programming with QuickDraw 3D 1.5.4
Previous | QD3D Book | Overview | Chapter Contents | Next |
Like the polyhedron and trimesh, the mesh is designed for representing polyhedra. However, it is intended for the interactive topological creation and editing of polyhedra, so its architecture and API were designed to support both iterative construction and topological modification.
Iterative construction means that you can easily construct a mesh by building it face-by-face, instead of filling in a data structure and constructing it from the data structure all at once.
Topological modification means that you can easily add and delete vertices, faces, edges, and other components in a mesh. A mesh has no explicit public data structure; unlike the other geometric primitives, it also has no immediate-mode capability.
Meshes are not intended for representing large-scale polyhedral models with many vertices and faces. If employed this way, the mesh format causes poor I/O behavior, heavy memory usage, and suboptimal rendering speed. Hence modeling, animation, and design applications should use the polyhedron format for most model creation and storage.
On the other hand, in some applications the mesh format is superior to other geometric primitives. For example, it would be ideal in an application that used a 3D sampling peripheral, such as a Polhemus device, to digitize physical objects. You could use the mesh to construct the digitized model face-by-face, to merge or split faces, to add or delete vertices, and so forth. Doing these tasks with an array-based data structure would be awkward to program and force the program to make repeated array reallocations.
The faces of meshes, unlike those of the polyhedron and trimesh, may have more than three vertices, may be concave (though not self-intersecting), and may contain holes by defining faces with more than one list of vertices.
The mesh API supports a rich variety of geometric and topological editing operations, but only for retained mode; it has no immediate-mode public data structure. If your application needs immediate mode, you should use the polyhedron format.
In general, the rendering speed of meshes is relatively slow. They must be either traversed for rendering or decomposed into other primitives that yield faster rendering. Traversing usually results in the slow retransformation and reshading of shared vertices, while decomposition may require heavy memory usage as well as complex and slow bookkeeping code.
To summarize, you should use the mesh primitive for interactive construction and topological editing. Its rich set of geometric and topological editing calls, the ability to make nontriangular faces directly, the ability to make concave faces and faces with holes, and the consistent use of attribute sets make the mesh primitive ideal for many purposes. In addition, the 3D metafile representation of a mesh is quite space efficient. Because the mesh lacks an immediate mode, however, it requires a large amount of memory and may be inefficient for other uses.
As explained in "Meshes" , you create a mesh by calling Q3Mesh_New to create a new empty mesh and then by calling Q3Mesh_VertexNew and Q3Mesh_FaceNew to explicitly add vertices and faces to the mesh. Listing 9 illustrates how to create a simple mesh using these functions. It also shows how to attach a custom surface parameterization to a mesh face, so that a texture can be mapped onto the face.
Listing 9 Creating a simple mesh
TQ3GroupObject MyBuildMesh (void)
{
TQ3ColorRGB myMeshColor;
TQ3GroupObject myModel;
static TQ3Vertex3D vertices[9] = {
{ { -0.5, 0.5, 0.0 }, NULL },
{ { -0.5, -0.5, 0.0 }, NULL },
{ { 0.0, -0.5, 0.3 }, NULL },
{ { 0.5, -0.5, 0.0 }, NULL },
{ { 0.5, 0.5, 0.0 }, NULL },
{ { 0.0, 0.5, 0.3 }, NULL },
{ { -0.4, 0.2, 0.0 }, NULL },
{ { 0.0, 0.0, 0.0 }, NULL },
{ { -0.4, -0.2, 0.0 }, NULL }};
static TQ3Param2D verticesUV[9] = {
{0.0, 1.0}, {0.0, 0.0}, {0.5, 0.0}, {1.0, 0.0},
{1.0, 1.0}, {0.5, 1.0}, {0.1, 0.8}, {0.5, 0.5},
{0.1, 0.4}};
TQ3MeshVertex myMeshVertices[9];
TQ3GeometryObject myMesh;
TQ3MeshFace myMeshFace;
TQ3AttributeSet myFaceAttrs;
unsigned long i;
myMesh = Q3Mesh_New(); /*create new empty mesh*/
Q3Mesh_DelayUpdates(myMesh); /*turn off mesh updating*/
/*Add vertices and surface parameterization to mesh.*/
for (i = 0; i < 9; i++) {
TQ3AttributeSet myVertAttrs;
myMeshVertices[i] = Q3Mesh_VertexNew(myMesh, &vertices[i]);
myVertAttrs = Q3AttributeSet_New();
Q3AttributeSet_Add(myVertAttrs, kQ3AttributeTypeSurfaceUV, &verticesUV[i]);
Q3Mesh_SetVertexAttributeSet(myMesh, myMeshVertices[i], myVertAttrs);
Q3Object_Dispose(myVertAttrs);
}
myFaceAttrs = Q3AttributeSet_New();
myMeshColor.r = 0.3;
myMeshColor.g = 0.9;
myMeshColor.b = 0.5;
Q3AttributeSet_Add(myFaceAttrs, kQ3AttributeTypeDiffuseColor, &myMeshColor);
myMeshFace = Q3Mesh_FaceNew(myMesh, 6, myMeshVertices, myFaceAttrs);
Q3Mesh_FaceToContour(myMesh, myMeshFace,
Q3Mesh_FaceNew(myMesh, 3, &myMeshVertices[6], NULL));
Q3Mesh_ResumeUpdates(myMesh);
myModel = Q3OrderedDisplayGroup_New();
Q3Group_AddObject(myModel, myMesh);
Q3Object_Dispose(myFaceAttrs);
Q3Object_Dispose(myMesh);
return (myModel);
}
The new mesh created by MyBuildMesh is a retained object. Note that you need to call Q3Mesh_New before you call Q3Mesh_VertexNew and Q3Mesh_FaceNew . Also, the call to Q3Mesh_FaceToContour destroys any attributes associated with the mesh face that is turned into a contour.
QuickDraw 3D supplies functions that you can use to traverse a mesh by iterating through various parts of the it. For example, you can operate on each face of a mesh by calling the Q3Mesh_FirstMeshFace function to get the first face in the mesh and then Q3Mesh_NextMeshFace to get each successive face. When you call Q3Mesh_FirstMeshFace , you specify a mesh and a mesh iterator structure, which QuickDraw 3D fills in with information about its current position while traversing the mesh. You must pass that same mesh iterator structure to Q3Mesh_NextMeshFace when you get successive faces in the mesh. Listing 10 illustrates how to use these routines to operate on all faces in a mesh.
Listing 10 Iterating through all faces in a mesh
TQ3Status MySetMeshFacesDiffuseColor (TQ3GeometryObject myMesh,
TQ3ColorRGB color)
{
TQ3MeshFace myFace;
TQ3MeshIterator myIter;
TQ3Status myErr;
TQ3AttributeSet mySet;
for (myFace = Q3Mesh_FirstMeshFace(myMesh, &myIter);
myFace;
myFace = Q3Mesh_NextMeshFace(&myIter)) {
/*Get the current attribute set of the current face.*/
myErr = Q3Mesh_GetFaceAttributeSet(myMesh, myFace, &mySet);
if (myErr == kQ3Failure) return (kQ3Failure);
/*Add the color attribute to the face attribute set.*/
myErr = Q3AttributeSet_Add((TQ3AttributeSet)mySet,
kQ3AttributeTypeDiffuseColor, &color);
if (myErr == kQ3Failure) return (kQ3Failure);
/*Set the attribute set of the current face.*/
myErr = Q3Mesh_SetFaceAttributeSet(myMesh, myFace, mySet);
if (myErr == kQ3Failure) return (kQ3Failure);
}
return (kQ3Success);
}
QuickDraw 3D also supplies a number of C language macros that you can use to simplify your source code when traversing a mesh. For example, you can use the following Q3ForEachMeshFace macro:
#define Q3ForEachMeshFace(m,f,i)
for ( (f) = Q3Mesh_FirstMeshFace((m),(i));
(f);
(f) = Q3Mesh_NextMeshFace((i)) )
Listing 11 shows how to use two of these macros to attach a corner to each vertex or each face of a mesh.
Listing 11 Attaching corners to all vertices in all faces of a mesh
TQ3Status MyAddCornersToMesh (TQ3GeometryObject myMesh,
TQ3AttributeSet mySet)
{
TQ3MeshFace myFace;
TQ3MeshVertex myVertex;
TQ3MeshIterator myIter1;
TQ3MeshIterator myIter2;
TQ3Status myErr;
Q3ForEachMeshFace(myMesh, myFace, &myIter1) {
Q3ForEachFaceVertex(myFace, myVertex, &myIter2) {
myErr = Q3Mesh_SetCornerAttributeSet
(myMesh, myFace, myVertex, mySet);
if (myErr == kQ3Failure) return (kQ3Failure);
}
}
return (kQ3Success);
}
Previous | QD3D Book | Overview | Chapter Contents | Next |